package com.syt.cellphone.information.help

import android.annotation.SuppressLint
import android.content.Context
import android.content.pm.PackageManager
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.util.Log
import com.syt.cellphone.information.utils.FileUtils
import org.jetbrains.anko.toast
import java.io.PrintWriter
import java.io.StringWriter
import java.text.SimpleDateFormat
import java.util.*
import java.util.concurrent.TimeUnit
import kotlin.collections.HashMap

/**
 *  @author: syt
 *  创建时间: 2020/7/31 16:25
 *  @功能   异常管理类
 */
class CrashHandler : Thread.UncaughtExceptionHandler{

    companion object {

        val TAG: String = this.javaClass.simpleName

        /**
         * 存储异常和参数信息
         */
        private val paramsMap = HashMap<String, String>()

        /**
         * 格式化时间
         */
        @SuppressLint("SimpleDateFormat")
        private val format = SimpleDateFormat("yyyy-MM-dd, HH-mm-ss")

        /**
         * 系统默认UncaughtExceptionHandler
         */
        private var mDefaultHandler: Thread.UncaughtExceptionHandler? = null

    }

    private var mContext: Context? = null

    fun init(context: Context) {
        mContext = context
        mDefaultHandler = Thread.getDefaultUncaughtExceptionHandler()
        // 设置该CrashHandler为系统默认的
        Thread.setDefaultUncaughtExceptionHandler(this)
    }

    override fun uncaughtException(p0: Thread, p1: Throwable) {
        handleException(p1)
        mDefaultHandler?.uncaughtException(p0, p1);
    }

    /**
     * 处理该异常
     */
    private fun handleException(ex: Throwable?) {
        if (ex == null) return
        // 搜集设备参数信息
        collectDeviceInfo(mContext!!)
        // 添加自定义信息
        addCustomInfo()
        kotlin.runCatching {
            // 使用Toast来显示异常信息
            Handler(Looper.getMainLooper()).post {
                ex.message?.let { mContext!!.toast(it) }
            }
        }
        // 保存日志文件
        saveCrashInfoFile(ex)
    }

    private fun collectDeviceInfo(ctx: Context) {
        // 获取versionName， versionCode
        // 运行捕获处理
        kotlin.runCatching {
            val pm = ctx.packageManager
            val pi = pm.getPackageArchiveInfo(ctx.packageName, PackageManager.GET_ACTIVITIES)
            if (pi != null) {
                val versionName = if (pi.versionName == null) "null" else pi.versionName
                val versionCode = pi.longVersionCode.toString()
                paramsMap["versionName"] = versionName
                paramsMap["versionCode"] = versionCode
            }
        }

        // 获得系统的信息
        val fields = Build::class.java.declaredFields
        kotlin.runCatching {
            for (field in fields) {
                field.isAccessible = true
                field.get(null)?.toString()?.let {
                    paramsMap[field.name] = it
                }
            }
        }
    }

    private fun addCustomInfo() {
        Log.i(TAG, "addCustomInfo: 程序出错了...")
    }

    /**
     * 保存错误信息到文件中
     */
    private fun saveCrashInfoFile(ex: Throwable) {
        kotlin.runCatching {
            val sb = StringBuilder()
            for ((key, value) in paramsMap) {
                sb.append(key).append("=").append(value).append("\n")
            }

            val writer = StringWriter()
            val printWriter = PrintWriter(writer)
            ex.printStackTrace(printWriter)
            var cause: Throwable? = ex.cause
            while (cause != null) {
                cause.printStackTrace(printWriter)
                cause = cause.cause
            }
            printWriter.close()
            val result = writer.toString()
            sb.append(result)
            val timestamp = System.currentTimeMillis()
            val time = format.format(Date())
            val fileName = "crash-$time-$timestamp.log"
            mContext?.externalCacheDir?.let { rootFile ->
                FileUtils.getDirFile(rootFile, "crash").listFiles()?.forEach {
                    if (it.lastModified() < System.currentTimeMillis() - TimeUnit.DAYS.toMillis(7)) {
                        it.delete()
                    }
                }
                FileUtils.createFileIfNotExist(rootFile, fileName, "crash")
                    .writeText(sb.toString())
            }
        }
    }
}